import sys
import wx
from threading import Event, currentThread

wxEVT_INVOKE = wx.NewEventType()

def EVT_INVOKE(win, func):
    win.Connect(-1, -1, wxEVT_INVOKE, func)
    
def DELEVT_INVOKE(win):
    win.Disconnect(-1, -1, wxEVT_INVOKE)

class InvokeEvent(wx.PyEvent):
    def __init__(self, func = None, args = None, kwargs = None):
        wx.PyEvent.__init__(self)
        self.SetEventType(wxEVT_INVOKE)
        self.func = func
        self.args = args
        self.kwargs = kwargs

class SafeInvocation:
    def __init__(self):
        if sys.platform[:3] == 'win':
            self.invokeLaterEvent = InvokeEvent()
            self.invokeLaterList = []
        EVT_INVOKE(self, self.onInvoke)

    if sys.platform[:3] == 'win':     # windows-only optimization
        def onInvoke(self, event):
            while self.invokeLaterList:
                func,args,kwargs = self.invokeLaterList[0]
                if self.doneflag.isSet():
                    return
                func(*args, **kwargs)
                del self.invokeLaterList[0]

        def invokeLater(self, func, args = [], kwargs = {}):
            if currentThread().getName() == "MainThread":
                func(*args, **kwargs)
            elif not self.doneflag.isSet():
                self.invokeLaterList.append((func, args, kwargs))
                if len(self.invokeLaterList) == 1:
                    wx.PostEvent(self, InvokeEvent(func, args, kwargs))
    else:
        def onInvoke(self, event):
            if not self.doneflag.isSet():
                event.func(*event.args, **event.kwargs)

        def invokeLater(self, func, args = [], kwargs = {}):
            if currentThread().getName() == "MainThread":
                func(*args, **kwargs)
            elif not self.doneflag.isSet():
                wx.PostEvent(self, InvokeEvent(func, args, kwargs))
